<template>
    <el-row :gutter="12">
      <!-- 左树 -->
      <el-col :span="8">
        <el-card shadow="never" body-style="padding:8px">
          <div class="toolbar">
            <el-input
              v-model="treeKw"
              placeholder="搜索名称/编码（本地过滤）"
              size="small"
              clearable
              @input="filterTree"
            />
            <el-button size="small" @click="reload(true)">刷新</el-button>
          </div>
  
          <el-tree
            v-loading="treeLoading"
            :data="treeData"
            node-key="id"
            :props="{ children: 'children', label: 'name' }"
            :highlight-current="true"
            :expand-on-click-node="false"
            accordion
            :filter-node-method="filterNode"
            ref="treeRef"
            @node-click="onNodeClick"
            :render-content="renderTreeNode"
          />
        </el-card>
      </el-col>
  
      <!-- 右表 -->
      <el-col :span="16">
        <el-card shadow="never" body-style="padding:8px">
          <div class="toolbar">
            <slot name="table-toolbar">
              <el-input
                v-model="innerQuery.name"
                placeholder="按名称搜索（回车）"
                clearable
                @keyup.enter.native="reload(false)"
                size="small"
                style="max-width: 220px"
              />
              <el-button size="small" type="primary" @click="reload(false)">查询</el-button>
              <el-button size="small" @click="resetQuery">重置</el-button>
            </slot>
          </div>
  
          <el-table
            v-loading="loading"
            :data="tableRows"
            height="620"
            border
            stripe
            :row-class-name="rowClassName"
            @row-dblclick="(row)=>$emit('row-dblclick', row)"
          >
            <el-table-column label="名称 / 编码" min-width="300">
              <template #default="{ row }">
                <div class="row-line">
                  <span class="name">{{ row.name }}</span>
                  <el-tag size="small" type="info">{{ row.code }}</el-tag>
                  <el-tag size="small" :type="typeColor(row.nodeType)">{{ typeText(row.nodeType) }}</el-tag>
                </div>
              </template>
            </el-table-column>
  
            <el-table-column prop="level" label="层级" width="70" align="center" />
  
            <el-table-column label="目录地址" min-width="320" show-overflow-tooltip>
              <template #default="{ row }">{{ breadcrumb(row) }}</template>
            </el-table-column>
  
            <el-table-column label="子项数量" width="90" align="center">
              <template #default="{ row }">{{ childCount(row.id) }}</template>
            </el-table-column>
  
            <!-- 你可以在父页面通过插槽继续追加列 -->
            <slot name="extra-columns"></slot>
  
            <el-table-column label="操作" width="180" align="center">
              <template #default="{ row }">
                <el-link type="primary" @click="$emit('view', row)">详情</el-link>
                <el-divider direction="vertical" />
                <el-link @click="$emit('add-child', row)">新增子项</el-link>
                <el-divider direction="vertical" />
                <el-link type="danger" @click="$emit('remove', row)">删除</el-link>
                <div><el-link @click="copyPath(row)">复制路径</el-link></div>
              </template>
            </el-table-column>
          </el-table>
  
          <div class="pager">
            <el-pagination
              background
              layout="total, prev, pager, next, sizes"
              :total="total"
              v-model:current-page="innerQuery.pageNum"
              v-model:page-size="innerQuery.pageSize"
              @current-change="reload(false)"
              @size-change="reload(false)"
            />
          </div>
        </el-card>
      </el-col>
    </el-row>
  </template>
  
  <script setup>
  import { ref, reactive, computed, watch, toRaw, nextTick } from 'vue'
  
  /**
   * Props
   *  - fetcher(params) -> Promise<{rows,total}>
   *  - query: 初始查询参数（会被深拷贝到内部）
   *  - treeFetcher?: 可选，懒加载树用；若不传则用 fetcher 拉“大页”构建树
   *  - treePageSize: 用 fetcher 构建树时的一次性页大小（默认 2000）
   */
  const props = defineProps({
    fetcher: { type: Function, required: true },
    query:   { type: Object,   default: () => ({ pageNum:1, pageSize:10 }) },
    treeFetcher: { type: Function, default: null },
    treePageSize: { type: Number, default: 2000 }
  })
  const emit = defineEmits(['update:selected', 'row-dblclick', 'view', 'add-child', 'remove', 'loaded'])
  
  /* ------- state ------- */
  const loading = ref(false)
  const total = ref(0)
  const rows = ref([])           // 当前页数据（表格）
  const allList = ref([])        // 构建树用的全量/大页
  const idMap = new Map()
  
  const treeLoading = ref(false)
  const treeData = ref([])
  const treeRef = ref(null)
  const currentNode = ref(null)
  const treeKw = ref('')
  
  /* ------- 内部查询对象（由 props.query 深拷贝） ------- */
  const innerQuery = reactive(JSON.parse(JSON.stringify(props.query || { pageNum:1, pageSize:10 })))
  
  /* ------- 对外双向绑定选中节点 ------- */
  watch(currentNode, v => emit('update:selected', v))
  
  /* ------- 计算：表格行（当前节点 + 直接子项） ------- */
  const tableRows = computed(() => {
    if (!currentNode.value) return []
    return [ currentNode.value, ...((currentNode.value.children) || []) ]
  })
  
  /* ====== 加载列表（分页） ====== */
  async function loadPage() {
    loading.value = true
    try {
      const { rows: r = [], total: t = 0 } = await props.fetcher(toRaw(innerQuery))
      rows.value = r
      total.value = t
      emit('loaded', { rows: r, total: t })
    } finally {
      loading.value = false
    }
  }
  
  /* ====== 加载树（全量或懒加载） ====== */
  async function loadTree() {
    treeLoading.value = true
    try {
      if (props.treeFetcher) {
        // 懒加载树（这里给一个最简单的“拉全量根+所有子”的默认实现占位，
        // 你也可以直接把 treeFetcher 实现成一次性返回全部。）
        const { rows: r = [] } = await props.treeFetcher()
        allList.value = r
      } else {
        // 直接用 fetcher 拉“大页”构树
        const bigParams = { ...toRaw(innerQuery), pageNum: 1, pageSize: props.treePageSize }
        const { rows: r = [] } = await props.fetcher(bigParams)
        allList.value = r
      }
      buildTree(allList.value)
      // 默认选中第一个根
      nextTick(() => {
        if (treeData.value[0]) {
          treeRef.value.setCurrentKey(treeData.value[0].id)
          onNodeClick(treeData.value[0])
        }
      })
    } finally {
      treeLoading.value = false
    }
  }
  
  /* ====== 构树 ====== */
  function buildTree(list) {
    idMap.clear()
    list.forEach(n => { n.children = []; idMap.set(n.id, n) })
    const roots = []
    list.forEach(n => {
      if (n.parentId && idMap.has(n.parentId)) {
        idMap.get(n.parentId).children.push(n)
      } else {
        roots.push(n)
      }
    })
    const sortNodes = arr => arr.sort((a,b)=>(a.orderNum||0)-(b.orderNum||0) || String(a.name).localeCompare(String(b.name)))
    const dfs = arr => { sortNodes(arr); arr.forEach(x=>dfs(x.children)) }
    dfs(roots)
    treeData.value = roots
  }
  
  /* ====== 过滤树 ====== */
  function filterNode(val, data) {
    if (!val) return true
    const s = String(val).toLowerCase()
    return (String(data.name||'').toLowerCase().includes(s) || String(data.code||'').toLowerCase().includes(s))
  }
  function filterTree(){ treeRef.value && treeRef.value.filter(treeKw.value) }
  
  /* ====== 行样式 / 类型显示 ====== */
  function rowClassName({ row }) {
    if (!currentNode.value) return ''
    return row.id === currentNode.value.id ? 'selected-row' : ''
  }
  function typeText(t){ return ({ SYSTEM:'系统', PARENT:'母设备', CHILD:'子设备', PART:'部件' }[t] || t) }
  function typeColor(t){ return ({ SYSTEM:'success', PARENT:'warning', CHILD:'info', PART:'' }[t] || 'info') }
  
  /* ====== 面包屑 / 子项数 ====== */
  function childCount(id){ const n = idMap.get(id); return n ? (n.children?.length || 0) : 0 }
  function breadcrumb(row){
    const ids = []
    if (row.ancestors) {
      ids.push(...String(row.ancestors).split(',').filter(Boolean).map(x=>Number(x)))
    } else if (row.path) {
      ids.push(...String(row.path).split('/').filter(Boolean).map(x=>Number(x))); ids.pop()
    } else if (row.parentId) {
      let p = idMap.get(row.parentId); const stack=[]
      while(p){ stack.unshift(p.id); p = idMap.get(p.parentId) }
      ids.push(...stack)
    }
    const names = ids.map(id => { const n=idMap.get(id); return n ? `${n.name}(${n.code||'—'})` : id })
    names.push(`${row.name}(${row.code||'—'})`)
    return names.join(' / ')
  }
  async function copyPath(row){ await navigator.clipboard?.writeText(breadcrumb(row)) }
  
  /* ====== 事件 ====== */
  function onNodeClick(node){ currentNode.value = node }
  
  /* ====== 对外方法 ====== */
  async function reload(refreshTree){
    await loadPage()
    if (refreshTree) await loadTree()
  }
  function resetQuery(){
    Object.assign(innerQuery, { pageNum:1, pageSize: (props.query?.pageSize || 10), name:'', code:'', nodeType:null })
    reload(false)
  }
  defineExpose({ reload, getCurrent: () => currentNode.value })
  
  /* ====== 初始化 ====== */
  reload(true)
  </script>
  
  <style scoped>
  .toolbar{ display:flex; gap:8px; align-items:center; margin-bottom:8px; }
  .row-line{ display:flex; align-items:center; gap:8px; }
  .row-line .name{ font-weight:600; }
  .selected-row{ background:#f0f9ff !important; }
  .pager{ display:flex; justify-content:flex-end; padding-top:8px; }
  </style>
  